package ${model.packageFullName};

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.junit.jupiter.api.Test;

import org.springframework.beans.BeanUtils;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.TestConstructor;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import ace.cmp.core.model.R;
import ace.cmp.id.generator.api.LongIdGenerator;
import ace.cmp.jmockdata.core.impl.util.JMockDataUtils;
import ace.cmp.json.api.JsonService;
import ace.cmp.security.oauth2.resource.server.test.service.MockTokenIntrospectService;
import ace.cmp.util.AceAssertUtils;
import ace.module.common.api.constant.SystemSecurityConstants;

import ${model.baseTestClassFullName};
import ${model.entityClassFullName};
import ${model.entityRepositoryClassFullName};
import ${model.restAdminControllerClassFullName};

/**
* ${model.entityRemark} rest admin controller unit test.
*
* @author Caspar
*/
@Slf4j
@AllArgsConstructor
public class ${model.className} extends BaseTest {
private final MockMvc mockMvc;
    private final LongIdGenerator idGenerator;
    private final JsonService jsonService;
    private final JMockDataUtils jMockDataUtils;
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final MockTokenIntrospectService mockTokenIntrospectService;

    private final ${model.entityRepositoryClassName} repository;
    private final Class<${model.entityIdType}> idClass = ${model.entityIdType}.class;
    private final Class<${model.entityClassName}> modelClass = ${model.entityClassName}.class;
    private final String URL_PREFIX = ${model.restAdminControllerClassName}.PATH;
    private final String SAVE_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_SAVE_URI;
    private final String SAVE_NOT_NULL_FIELD_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_SAVE_NOT_NULL_FIELD_URI;
    private final String ADD_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_ADD_URI;
    private final String ADD_NOT_NULL_FIELD_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_ADD_NOT_NULL_FIELD_URI;
    private final String MODIFY_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_MODIFY_URI;
    private final String MODIFY_NOT_NULL_FIELD_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_MODIFY_NOT_NULL_FIELD_URI;
    private final String REMOVE_BY_ID_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_REMOVE_BY_ID_URI;
    private final String REMOVE_LOGIC_BY_ID_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_REMOVE_LOGIC_BY_ID_URI;
    private final String GET_BY_ID_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_GET_BY_ID_URI;
    private final String GET_LIST_BY_ID_IN_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_GET_LIST_BY_ID_IN_URI;
    private final String GET_MAP_BY_ID_IN_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_GET_MAP_BY_ID_IN_URI;
    private final String SEARCH_URI = URL_PREFIX + ${model.restAdminControllerClassName}.ADMIN_SEARCH_URI;
  	private final String SECURITY_PERMISSION_PREFIX = "${model.source.classSecurityPermissionPrefix}";
    private final String SECURITY_PERMISSION_READ = SECURITY_PERMISSION_PREFIX + SystemSecurityConstants.ADMIN_SECURITY_AUTHORITY_SUFFIX + SystemSecurityConstants.READ_SECURITY_AUTHORITY_SUFFIX;
    private final String SECURITY_PERMISSION_WRITE = SECURITY_PERMISSION_PREFIX + SystemSecurityConstants.ADMIN_SECURITY_AUTHORITY_SUFFIX + SystemSecurityConstants.WRITE_SECURITY_AUTHORITY_SUFFIX;
    private final String SECURITY_PERMISSION_REMOVE = SECURITY_PERMISSION_PREFIX + SystemSecurityConstants.ADMIN_SECURITY_AUTHORITY_SUFFIX + SystemSecurityConstants.REMOVE_SECURITY_AUTHORITY_SUFFIX;


	  @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @SneakyThrows
    @Test
    public void test_save() throws Exception {

        ${model.entityClassName} model = this.getMockModel();
        model.setId(null);
        model.setRowVersion(null);
        String jsonString = jsonService.toJson(model);
        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .post(SAVE_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        ${model.entityIdType} id = this.getIdFromResponseBody(mvcResult);
        ${model.entityClassName} savedModel = repository.getById(id);
        AceAssertUtils.isNull(savedModel);

        Thread.sleep(RandomUtils.nextInt(1, 500));
        ${model.entityClassName} updateModel = new ${model.entityClassName}();
        BeanUtils.copyProperties(savedModel,updateModel);

        jsonString = jsonService.toJson(updateModel);

        mockMvc.perform(
                MockMvcRequestBuilders
                        .post(SAVE_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        id = this.getIdFromResponseBody(mvcResult);

        ${model.entityClassName} modifyModel = repository.getById(id);
        log.info("savedModel:{}", savedModel);
        log.info("updateModel:{}", updateModel);
        log.info("modifyModel:{}", modifyModel);
        AceAssertUtils.isTrue(updateModel.getUpdateTime().equals(modifyModel.getUpdateTime()));
        AceAssertUtils.isTrue(savedModel.getUpdateTime().equals(modifyModel.getUpdateTime()));
    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @SneakyThrows
    @Test
    public void test_saveNotNull() throws Exception {

        ${model.entityClassName} model = this.getMockModel();
        model.setId(null);
        model.setRowVersion(null);
        String jsonString = jsonService.toJson(model);
        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .post(SAVE_NOT_NULL_FIELD_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        ${model.entityIdType} id = this.getIdFromResponseBody(mvcResult);
        ${model.entityClassName} savedModel = repository.getById(id);
        AceAssertUtils.isNull(savedModel);

        Thread.sleep(RandomUtils.nextInt(1, 500));

        ${model.entityClassName} updateModel = new ${model.entityClassName}();
        BeanUtils.copyProperties(savedModel,updateModel);

        jsonString = jsonService.toJson(updateModel);

        mockMvc.perform(
                MockMvcRequestBuilders
                        .post(SAVE_NOT_NULL_FIELD_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        id = this.getIdFromResponseBody(mvcResult);

        ${model.entityClassName} modifyModel = repository.getById(id);
        log.info("savedModel:{}", savedModel);
        log.info("updateModel:{}", updateModel);
        log.info("modifyModel:{}", modifyModel);
        AceAssertUtils.isTrue(updateModel.getUpdateTime().equals(modifyModel.getUpdateTime()));
        AceAssertUtils.isTrue(savedModel.getUpdateTime().equals(modifyModel.getUpdateTime()));
    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @Test
    public void test_add() throws Exception {
        ${model.entityClassName} model = this.getMockModel();

        String jsonString = jsonService.toJson(model);

        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .post(ADD_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        ${model.entityIdType} id =this.getIdFromResponseBody(mvcResult);

        ${model.entityClassName} savedModel = repository.getById(id);
        AceAssertUtils.isNull(savedModel);
    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @Test
    public void test_addNotNull() throws Exception {
        ${model.entityClassName} model = this.getMockModel();

        String jsonString = jsonService.toJson(model);

        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .post(ADD_NOT_NULL_FIELD_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        ${model.entityIdType} id = this.getIdFromResponseBody(mvcResult);

        ${model.entityClassName} savedModel = repository.getById(id);
        AceAssertUtils.isNull(savedModel);
    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @Test
    public void test_modify() throws Exception {
        ${model.entityClassName} addModel = this.getMockModel();
        this.repository.insert(addModel);

        ${model.entityClassName} savedModel = repository.getById(addModel.getId());

        Thread.sleep(RandomUtils.nextInt(1, 500));
        savedModel.setUpdateTime(System.currentTimeMillis());

        String jsonString = jsonService.toJson(savedModel);

        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .post(MODIFY_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        ${model.entityIdType} id = this.getIdFromResponseBody(mvcResult);

        ${model.entityClassName} savedModel2 = repository.getById(id);
        AceAssertUtils.isTrue(savedModel.getUpdateTime().equals(savedModel2.getUpdateTime()));
    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @Test
    public void test_modifyNotNull() throws Exception {
        ${model.entityClassName} addModel = this.getMockModel();
        this.repository.insert(addModel);

        ${model.entityClassName} savedModel = repository.getById(addModel.getId());

        Thread.sleep(RandomUtils.nextInt(1, 500));
        ${model.entityClassName} updateModel = new ${model.entityClassName}();
        BeanUtils.copyProperties(savedModel, updateModel);

        String jsonString = jsonService.toJson(updateModel);

        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .post(MODIFY_NOT_NULL_FIELD_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        ${model.entityIdType} id = this.getIdFromResponseBody(mvcResult);

        ${model.entityClassName} savedModel2 = repository.getById(id);
        AceAssertUtils.isTrue(updateModel.getUpdateTime().equals(savedModel2.getUpdateTime()));
        AceAssertUtils.isTrue(savedModel.getUpdateTime().equals(savedModel2.getUpdateTime()));
    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @Test
    public void test_remove() throws Exception {
        List<${model.entityClassName}> addModels = Arrays.asList(
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel()
        );
        repository.insert(addModels);

        List<${model.entityIdType}> request = Arrays.asList(addModels.get(0).getId(), addModels.get(1).getId(), addModels.get(2).getId());

        String jsonString = jsonService.toJson(request);

        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .post(REMOVE_BY_ID_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        request.stream()
                .forEach(id -> {
                    ${model.entityClassName} savedBook2 = repository.getById(id);
                    AceAssertUtils.isNotNull(savedBook2, "");
                });

    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @Test
    public void test_removeLogic() throws Exception {
        List<${model.entityClassName}> addModels = Arrays.asList(
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel()
        );

        repository.insert(addModels);

        List<${model.entityIdType}> request = Arrays.asList(addModels.get(0).getId(), addModels.get(1).getId(), addModels.get(2).getId());

        String jsonString = jsonService.toJson(request);

        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .post(REMOVE_LOGIC_BY_ID_URI)
                        .content(jsonString)
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        request.stream()
                .forEach(id -> {
                    ${model.entityClassName} savedBook2 = repository.getById(id);
                    AceAssertUtils.isNull(savedBook2, "");
                    AceAssertUtils.isFalse(savedBook2.getDeleteFlag().equals(id));
                });

    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @Test
    public void test_get() throws Exception {

        List<${model.entityClassName}> addModels = Arrays.asList(
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel()
        );

        repository.insert(addModels);

        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .get(GET_BY_ID_URI + "?id=" + addModels.get(0).getId().toString())
                //   .queryParam("id", addModels.get(0).getId().toString())
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andExpect(MockMvcResultMatchers.jsonPath("data.id").value(addModels.get(0).getId()))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();
    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @Test
    public void test_list() throws Exception {
        List<${model.entityClassName}> addModels = Arrays.asList(
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel()
        );

        repository.insert(addModels);

        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .get(GET_LIST_BY_ID_IN_URI)
                        .queryParam("ids", String.format("%s,%s", addModels.get(0).getId().toString(), addModels.get(1).getId().toString()))
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andExpect(MockMvcResultMatchers.jsonPath("data.size()").value(2))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();
    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @Test
    public void test_map() throws Exception {
        List<${model.entityClassName}> addModels = Arrays.asList(
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel()
        );

        repository.insert(addModels);

        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .get(GET_MAP_BY_ID_IN_URI)
                        .queryParam("ids", String.format("%s,%s", addModels.get(0).getId().toString(), addModels.get(1).getId().toString()))
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andExpect(MockMvcResultMatchers.jsonPath(String.format("data.%s.id", addModels.get(0).getId())).value(addModels.get(0).getId()))
                .andExpect(MockMvcResultMatchers.jsonPath(String.format("data.%s.id", addModels.get(1).getId())).value(addModels.get(1).getId()))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();
    }

    @WithMockUser(authorities = {SECURITY_PERMISSION_READ, SECURITY_PERMISSION_WRITE, SECURITY_PERMISSION_REMOVE})
    @SneakyThrows
    @Test
    public void test_search() {
        List<${model.entityClassName}> addModels = Arrays.asList(
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel(),
                this.getMockModel()
        );

        repository.insert(addModels);

        String queryAll = String.format("id=in=(%s)", addModels.stream().map(p -> p.getId().toString()).collect(Collectors.joining(",")));
        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders
                        .get(SEARCH_URI)

                        .accept(MediaType.APPLICATION_JSON)
                        .queryParam("query", queryAll)
                        .queryParam("page", "0")
                        .queryParam("size", "1")
                        .queryParam("sort", "id,asc")
        )
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("code").value(0))
                .andExpect(MockMvcResultMatchers.jsonPath("data.totalElements").value(5))
                .andExpect(MockMvcResultMatchers.jsonPath("data.totalPages").value(5))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();
    }


    private ${model.entityClassName} getMockModel() {
        return this.jMockDataUtils.mock(modelClass);
    }

    @SneakyThrows
    private ${model.entityIdType} getIdFromResponseBody(MvcResult mvcResult) {
        String responseBody = mvcResult.getResponse().getContentAsString();
        TypeReference<R<${model.entityIdType}>> typeReference = new TypeReference<>() {
        };
        R<${model.entityIdType}> responseModel = objectMapper.readValue(responseBody, typeReference);
        ${model.entityIdType} id = responseModel.getData();
        return id;
    }
}
